3. Texter och

konsol I/O

 

·     Teckensträngar.
 

·     puts().

·     gets().

 

·     printf().

·     scanf().

·     Formateringssträngar.

·     Iostream.h.

 

·     Cout.

 

·     Cin.

 

·     Smart cin-användning.

 

 

 

 

 

 

 


Teckensträngar        

 

Som du kanske lade märke till fanns det ingen datatyp för text, bara en för enstaka tecken (char). Hur lagrar man då en text? Svaret är enkelt: bokstav för bokstav. För att sedan kunna komma åt texten på ett enkelt sätt bör man ha ett enda namn på alla char-variabler som texten består av. Detta åstadkommer vi genom att lagra tecknen i en ‘lista’, vilket vi kommer att lära mer om senare. För stunden räcker det att veta hur vi deklarerar en lista av typen char:

 

char <variablenamn>[<max antal tecken + 1>];

 

T.ex:

 

char MinText[22];

char MittNamn[11] = ”Håkan Berg”;

 

Sedan kan man använda strängen t.ex. i puts():

 

puts(MittNamn);

 

Hur kunde jag nu få mitt namn till att bli 11 tecken? Till att börja med måste vi räkna med mellanslaget, med då blir det bara 10 tecken. Sedan var det ju så att C inte hade någon datatyp för textsträngar, utan varje funktion som använder en sådan arbetar mot en  lista med ett visst antal element, ett för varje tecken. Hur ska då det programmet veta när texten är slut? Det kan ju egentligen bara fortsätta att läsa i minnet och tolka all data som ASCII-text, vare sig det är text, numeriska värden eller program. Lösningen är att reservera ett speciellt tecken som textslut, och man har valt värdet noll (inte siffran ”0”, utan det numeriska värdet noll). Detta lägger man till i slutet av texten (det elfte tecknet i exemplet ovan), varvid funktionen puts() vet att sluta i tid.

 

Minnet i datorn kommer på den plats som reserverats under det logiska namnet ”MittNamn” och framåt att innehålla:

 

48 7D 6B 61 6E 20 42 65 72 67 00

 


puts()

 

C har inget eget kommando för utmatning av data på konsolen (bildskärmen), men precis som med mycket annat som språket C inte innehåller har man lagt till det i form av funktioner, vilka levererats med i diverse headerfiler.

För ut och inmatning av texter har man skapat funktionerna puts() respektive gets(), där ‘s’ står för ‘string’.

 

Puts() är en funktions om matar ut en hel teckensträng (avslutad med NULL) som en rad, d.v.s. avslutar med nyradstecken. Texten skrivs i parantesen, mellan dubbelfnuttar.

 

Exempel:

 

#include <stdio.h>

void main()

{

   char sText[] = "This is a statement of some kind";

   puts("Nu tänker jag skriva ut variabeln sText:");

   puts(sText);

}

 

       

 

Observera att puts() automatiskt ger ny rad!

 

 

 

Övningsuppgift:

 

·      Skapa ett projekt text.mak.

·      Skriv ett program med en char-lista innehållande ditt namn. I main() ska du skriva ut ”Mitt namn är ” plus ditt namn från char-listan.

·      Spara källkoden som text.c och inkludera i projektet.

·      Kompilera, länka och testa.

 

 

 

 

? puts() Lägg märke till att hjälpen talar om att du behöver <stdio.h>.


gets()

 

Gets() är en funktions om hämtar in en hel teckensträng (avslutas med NULL) som en rad, d.v.s. fram till man trycker på ‘enter’. Strängvariabeln skrivs i parantesen och ska kunna förstås som en pekare (mer om det senare).

 

Exempel:

 

#include <stdio.h>

void main()

{

   char sText[81];

   puts("Skriv nu något intelligent på en rad!");

   gets(sText);

   puts("Ha! Jag såg nog att du skrev:");

   puts(sText);

}

         

 

 

Övningsuppgift:

 

·      Öppna projektet text.mak om det inte redan är öppet.

·      Ändra programmet så att det frågar efter ditt namn i stället för att du har det ‘hårdkodat’ i programmet.

·      Kompilera, länka och testa.

 

 

 

 

 

 

 

 

 

 

 

 

? gets().


printf()

 

För in och utmatning av data av valfritt format (även text) har man gjort funktionerna printf() och scanf(). Namnet printf() står för ”print formatted” d.v.s. skriv ut enligt visst format.

 

printf() är en funktion (vilket syns på paranteserna) och den finns i headerfilen stdio.h. Denna funktion är av typen void (d.v.s. lämnar inget svar), men den behöver naturligtvis inparametrar.

 

Parameter 1: Textsträng som beskriver utskriftsformat.

Parameter 2-n: Variabler där data hämtas till utskriften.

 

Parameter 1 är en textsträng. I denna sträng kan man ha med valfri text som ser ut som den ska se ut på skärmen. Den kan också innehålla styrtecken typ ”ny rad”, ”tabulator” etc. Dessutom markerar man platshållare för de variabler vars värden ska skrivas ut. Dessa platshållare markerar också på vilket sätt variabelns data ska visas. Strängen kallas för en formateringssträng.

 

Parameter 2-n är namn på variabler som innehåller de data som ska passas in vid respektive platshållare. De måste komma i samma ordning.

 

#include <stdio.h>

 

void main()

{

   int iSiffra = 73;

   printf("Innehållet i iSiffra = %d\n", iSiffra);

   iSiffra +=5;

   printf("Innehållet i iSiffra = %d\n", iSiffra);

}

 

Parameter1 är i detta fall textsträngen ”Innehållet i iSiffra = %d\n” och parameter2 är iSiffra. Det finns i detta fall bara 2 parametrar.

 

Övningsuppgift:

 

·      Skapa ett projekt printf.mak.

·      Lägg till ovanstående källkod som printf.c.

·      Kompilera, länka och testa.

·      Prova att ta bort \n i första printf()-satsen.

 

 

? Printf().


scanf()

 

På samma sätt som du skrev ut på skärmen med printf() kan du läsa in från tangentbordet med funktionen scanf(). På grund av att scanf() använder pekare (som vi ska lära mer om senare) måste man ange ett &-tecken framför de variabler som inte är strängvariabler.

 

Studera nedanstående exempel:

 

#include <stdio.h>

 

void main()

{

   float fTal = 1;

   float fKvadrat;

        

   printf("Skriver du ett tal så skriver jag dess kvadrat.\n");      

   while (fTal > 0)

   {

      printf("\nAnge ett tal: ");

      scanf("%f",&fTal);

      if (fTal > 0)

      {

         fKvadrat = fTal * fTal;

         printf("\nKvadraten på %g är %g.", fTal, fKvadrat);

      }

   }

}

 

Observera att scanf() stannar vid mellanslag, tabulator etc. i stället för vid radslut. Om man skriver flera ord men bara har en strängvariabel i formateringssträngen kommer scanf() att strunta i resten till nästa eventuella scanf()-sats kommer och fångar upp nästa ord etc. Nedanstående slinga kommer att genomlöpas flera gånger om man skriver en mening:

 

while (<avslutningsvillkor>)

{

   scanf(”%s”, sNamn);

   printf(”Du skrev: %s\n”, sNamn);

}

? scanf()                   

Om man kanske har något ‘skräp’ kvar som man inte vill använda, och vill vara säker på att detta är borta vid ny inmatning, kan man använda funktionen fflush(stdin). Slå upp fflush i hjälpen och titta på exemplet. Ofta har man kvar ett nyradstecken när man matat in något till en char-variabel, vilket du kommer att få problem med när du löser uppgifterna i kapitel 4.

 

När man använder ‘cin’ och dess metod ‘getline()’ (cin.getline()) kan samma problem uppstå. Då använder man en annan metod i cin, nämligen ‘ignore()’. Denna funktion tar ett argumente för antal radslut som ska ignoreras, vanligen ett, samt ett valfritt argument för att omdefiniera radslutstecknet. Det sista är förvalt till ‘\n’. Om man t.ex. vill ta bort ett kvarvarande radslut (vilket kan finnas om man har använt syntaxen ”cin >> <variabel>;”) skriver man bara ‘cin.ignore(1)’.

 

Övningsuppgift:

 

·      Öppna projektet printf.

·      Skapa en ny fil.

·      Skriv ett program som frågar efter två tal och svarar med deras kvot.

·      Spara källkoden under namnet division.c.

·      Ändra i projektet så att det använder division.c.

·      Kompilera, länka och testa.

 

Överkursuppgift:

 

·      Öppna projektet printf.

·      Skapa en ny fil.

·      Skriv ett program för multiplikationstabeller.

·      Det ska fråga efter ett tal från 1 till 12 och skriva ut tabellen.

·      Tabellen ska ha rubriken ”Multiplikationstabell nummer <tal>”.

·      Varje rad: ”<tal> * <tal> = <produkt>”.

·      Spara källkoden under namnet multtab.c.

·      Ändra i projektet så att det använder multtab.c.

·      Kompilera, länka och testa.

 

Observera att .exe-filen namnges efter projektet. Detta innebär när man ‘återanvänder’ ett projekt att .exe-filen byts ut. Det föregående programmet kan inte längre köras.

 

Detta kan vara användbart när man testar korta program som man inte avser att spara, eller medan man lär sig. Ska man skapa projekt som ska behållas bör man skapa ett nytt projekt för varje program man vill skriva.

 

? fflush., ignore - istream::ignore.

Formateringssträngar.

 

Formateringssträngen är en textsträng. Funktionen printf() analyserar den dock för att separera ut tre olika sorters information: Text som ska skrivas ut, plats­hållare för variabler samt styrkoder för utskriften. Platshållare känns igen på att de börjar med ett %-tecken, medan styrkoderna börjar med ett \-tecken. Allt annat är text som skrivs ut som den står.

 

De olika ”platshållarna” för de variablerna som ska skrivas ut betyder:

 

                 %d            decimalt heltal

                 %c            ett enstaka tecken

                 %s             teckensträng

                 %e             flyttal, exponentangivelse (med e)

                 %f             flyttal, decimalbråk

                 %g            den kortaste av %e och %f

                 %u            obetecknat decimalt heltal

                 %o            obetecknat oktalt heltal

                 %x            obetecknat hexadecimalt heltal

 

Observera att parameter 2-n är motsvarande variabler, och måste komma i samma ordning som platshållarna i formateringssträngen.

 

De olika styrkoderna (de s.k. escapesekvenserna) har följande betydelse:

 

                 \n               ny rad

                 \t               tabulator

                 \b              bakåtsteg

                 \r               vagnretur (börja om på samma rad)

                 \a               bell (numera piiip)

 

Nu är ytterligare två tecken reserverade (observera att du använder kommatecken till att separera parametrar, kaninöron att begränsa textsträng etc.). Hur gör man då om man vill skriva ut ett reserverat tecken? Jo, sätt till \-tecknet framför, t.ex:

 

                 \\               ger ett snedstreck

                 \’               apostrof

                 \”               dubbelfnutt

                 \x25           procenttecken (asciikod i hexadecimal notation)


Vi tittar på följande exempel:

 

#include <stdio.h>

 

void main()

{

  int iBokstav = 65;

  while (iBokstav < 90)

  {  

    printf("ASCII-koden för %c = %d\n", iBokstav, iBokstav);

    iBokstav++;

  }

}

 

                       

 

Som synes tolkas iBokstav olika beroende av vad vi skrev för typ av platshållare: %c gör att det numeriska värdet tolkas som ett tecken medan %d gör att det tolkas som ett heltal.

 

(Lägg märke till hur lätt det var att räkna upp ett heltalsvärde: iBokstav++ ökar talet i iBokstav med 1.)

 

 

Övningsuppgift:

 

·      Öppna projektet printf.mak.

·      Skriv in ovanstående källkod under namnet ascii.c.

·      Ändra i projektet så att det använder ascii.c.

·      Kompilera, länka och testa.


Modifierare:

 

Ibland behövs ytterligare information för hur data skrivs ut vid en platshållare, t.ex. antal bokstäver i en strängvariabel, eller antalet siffror som ska visas. Detta anges direkt efter %-tecknet före typbokstaven, utan mellanliggande mellanslag. Observera att inledande nollor inte visas, utan tar plats i utskriften som blanktecken. Detta för att kunna göra utskrifter med snyggt tabellutseende.

 

                 siffra          minsta antalet siffror som ska visas (inkl. decimaler)                           observera att inledande nollor inte visas utan skrivs ut                         som blanktecken.

                 .siffra         antal decimaler som ska visas eller antal tecken som                            visas ur en textsträng

                 -                vänsterjusterar utskrift av siffror

                 l                 datat är av typen long

                

Exempel:

                 %5d          visar minst fem siffror (data tolkas numeriskt)

                 %3.2f        visar minst tre siffror varav två är decimaler

                 %-6d         visar minst 6 siffror vänsterjusterat

                 %ld           visar data som long

                 %25s         visar en textsträng med minst 25 tecken

                 %10.5        visar 5 tecken ut en textsträng som minst 10 positioner

 

Vi testar:

int iTal;

...

printf(”>%4d<”,iTal);

 

Om iTal är:                  Så blir utskriften:

5                >   5<

100              > 100<

5468             >5468<

32000            >32000<

 

Vi testar:

float fTal;

...

printf(”>%-5.2f<”,fTal);

 

Om fTal är:                                   Så blir utskriften:

5.28             >5.28 <

12               >12.00<

1234             >1234.00<

.25              >0.25 <


Vi testar:

 

#define VERSALER ”ABCDEFGHIJKLMNOPQRSTUVWXYZ”

...

printf()-satsen:                                       Ger då utskriften:

printf(”>%2s<”, VERSALER);             >ABCDEFGHIJKLMNOPQRSTUVWXYZ<

printf(”>%20s<”, VERSALER);            >ABCDEFGHIJKLMNOPQRSTUVWXYZ<

printf(”>%10.5s<”, VERSALER);  >     ABCDE<

printf(”>%-10.5s<”, VERSALER); >ABCDE     <

 

Övningsuppgift:

 

·      Gör ett program som frågar vad du heter och svarar med ett positivt omdömme där det inmatade namnet ingår. Spara i filen namn.c.

·      Skapa projektet namn.mak och testa programmet.

 

Övningsuppgift:

 

·      Gör ett program som utför ovanstående demonstrationer (från sid 10 och 11). Spara i filen demo.c.

·      Skapa projektet demo.mak och testa programmet.

 

Överkursuppgift:

 

·      Titta i kapitel 4 och läs om programinstruktionen for. Du behöver den för att lösa denna uppgift.

·      Gör ett program som räknar ut antalet sädeskorn på ett schackbräde enligt det gamla exemplet med ett korn på första rutan, två på andra, fyra på tredje etc, och spara i filen korn.c.

·      Utskriften ska ge en rad för varje ruta: Ruta nr ... innehåller ... sädeskorn.

·      Använd projektet printf.mak för att testa programmet.

 

Överkursuppgift:

 

·      Titta i kapitel 4 och läs om programinstruktionen while. Du behöver den för att lösa denna uppgift.

·      Gör en ”reklamscroller” i filen reklam.c. Man ska kunna mata in texten.

·      En reklamscroller ser ut som en viss skärmsparare i windows, en valfri text som rullar från vänster till höger och kommer in igen från vänster.

·      Gör det som ett nytt projekt reklam.mak.

 

 

Observera att man kan ha många olika projekt i samma katalog. Fördelen med att skapa nya projektfiler är den att man inte skriver över de gamla .exe-filerna. Nackdelen blir uppenbar när man blandat tillräckligt många projekt i samma katalog.

Iostream.h

 

C++ levereras med ett stort antal nya funktioner. De gamla klumpiga in och utmatningsrutinerna du nyss läst om från stdio.h har nya, förbättrade motsvarigheter i iostream.h.

 

Om man tittar i iostream.h känner man inte igen de vanliga deklarationerna, det finns inga funktionsprototyper. I stället finns där hänvisningar till ios.h, istream.h och ostream.h samt en klassdefinition bland annat.

 

Iostream är alltså en klass, eller rättare sagt en hel klasshierarki. Nedanstående bild visar hur hierarkin ser ut:

 

 

 

 

 

 

Detta ser kanske lite ovant ut just nu, men när vi lärt mer om klasser kommer ovanstående hierarki att få sin mening. Du ska bara veta att du har tillgång till allt detta om du skrivit #include <iostream.h> i ditt program.

 

 

? iostream, ios, istream, ostream.


Cout.

 

Nu kan vi äntligen koncentrera oss mer på programmering än på formaterings­strängar. Cout är en medlemsfunktion i klassen ostream, vilken tar emot variabler och data via den överladdade operatorn <<. Vad vi behöver veta är egentligen bara hur vi ska använda den. Det är en av finesserna med OOP. Om man vill skriva ut en text kan man skriva:

 

cout << ”Detta ska skrivas ut”;

 

Man kan också skriva ut en variabel:

 

cout << cText;

 

Eller flera stycken:

 

cout << ”I dag är det ” << fTemp << ” grader!” << ‘\n’;

 

Ska man skriva mycket, kan man göra det på flera rader:

 

cout << ”Detta är början på en ” << iAntTkn

     << ” lång rad, och längre kanske den blir.\n”;

 

Observera att cout inte lägger till nyrad själv, precis som printf().

 

Operatorerna ‘<<‘, och ‘>>‘ som vi lär oss om strax, kallas populärt för ‘strömningsoperatorer’.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

? iostream - iostream Programming.


Cin.

 

Ännu bättre blir det när vi ska göra inmatning. Man behöver inte skriva adressen till den variabel man vill att det inmatade värdet ska hamna i, man behöver bara skriva den:

 

cin >> iAntal;

 

Observera att strömningsoperatorn pekar mot variabeln. Data strömmar från cin till iAntal. Man kan även mata in flera variabler samtidigt:

 

cin >> iAntal >> iStorlek;

 

 

Övningsuppgift:

·      Skriv ett program vikt som frågar efter namn, längd och vikt på en person.

·      Programmet ska sedan beräkna om du väger för mycket, för litet eller lagom, och skriva ut det på följande sätt:
Hör nu här <namn>, jag tycker att du väger för mycket/alldeles lagom/för litet!

·      Man bör väga c:a (+/- 5 kg) lika många kilo som man är centimeter lång över metern. (T.ex: längd 1.45 bör väga 40 till 50 kg.)

·      Använd cin och cout!

·      Så här kan det se ut (om man sköter sin vikt!):

 

 

          

 

 

 

 

 

 

 

? [help - search] - query: iostream cin return - ‘How to clear...’, ‘Input member...’ samt ‘ios’ (Observera att Query-fönstret kan nålas fast!)


Smart cin-användning.

 

Cin returnerar faktiskt ett svar. Du behöver inte använda det, men om du vill kan du t.ex. testa på det för att styra bearbetningen. Följande programslinga avbryts när man skriver in något icke tillåtet:

 

cout << ”Ange tal (‘a’ avslutar):\n”

while(cin >> iVar)       // iVar är av typen int!

{

   // Gör dina beräkningar och utskrifter.

   ...

   ...

}

 

 

Övningsuppgift:

·      Gör ett program multi2 som använder ovanstående metod att ta emot 2 tal, multiplicera dem, visa resultatet och fråga efter nästa talpar tills man svara med en felaktig datatyp (bokstaven s är av typen char, inte int).

 

 

 

 

                     

 

 

Observera att returvärdet från cin inte kan mellanlagras i en heltalsvariabel. Returtypen är faktiskt en referens till ett objekt av typen class iostream. Detta får man reda på om man försöker spara returvärdet i en vanlig variabel. Kompil­atorn klagar i så fall på att den inte kan omvandla returvärdet från class iostream till den typ man använt. Däremot har denna referens värdet noll om cin miss­lycka­des med sin uppgift, vilket duger som villkor för t.ex. ‘while’.